/****************************************************************************
*  Title: nsa4400.sc
*  Author: Chandra Garrepally
*  Date: 
*  Description: nsa4400 Specification Model
****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

import "c_queue";
import "c_double_handshake";
import "ofdm";
import "desFunction";
import "desScrunch";
import "desUnscrun";
import "desKeyGen";

#define EN0 0

// c1: key, fifo1: data block
behavior Stimulus(i_sender c1, i_sender fifo1, in event done)  
{   
  const char* bName = "Stimulus";
  void main(void)  
  {
    const unsigned long blockSize = 8;
    const unsigned long numBlocks = 8;
    const unsigned long dataSize = blockSize * numBlocks;

    unsigned int i = 0;
    unsigned int j = 0;

    // Key
    unsigned char key[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
    short edf = EN0;

    unsigned char data[dataSize];

    // Fill the blocks with data
    for (i = 0; i < dataSize ; i++) { data[i] = i + '0'; }
    
    // Send the key and edf values in a message to the design behavior
    printf("%s: Sending key and edf values to Design...\n", bName);
    for (i=0; i<8; i++) {
    c1.send(key+i, sizeof(char));}
    c1.send(&edf, sizeof(short));

    // Send the data (words) using the out fifo1 port to the design behavior.
    for ( i = 0 ; i < numBlocks ; i++)
    {
       puts("------------------------------------");
       printf("%s: Sending block #%d\n", bName, i);
       puts("------------------------------------");
       printf ("%s: data [%i..%i] : ", bName, i, i+7);
       for (j = 0 ; j < blockSize ; j++)
       {
           printf("0x%x  ", data[i*8+j]);
           fifo1.send(data+(i*8+j), sizeof(char));
        }
       wait (done);
       waitfor (100);
    }

    puts("Simulation done....");
    exit(0);
  }
};

behavior Des(i_receiver c1, i_receiver fifo1, 
             i_sender is)
{
  // Communication Channels, could be c_queue as well 
  c_double_handshake c2; // to pass keys from key generator to des function
  c_double_handshake c3; // to pass data from scrunch to des function
  c_double_handshake c4; // to pass data from des function to unscrunch 

  // Behavior instances
  desKeyGen keyGeneration(c1, c2);
  desScrunch desScrunching(fifo1, c3);
  desFunction desEncryptDecrypt(c2, c3, c4);
  desUnscrun desUnscrunching(c4, is);

  void main()
  {
    par
    {
      keyGeneration.main();
      desScrunching.main();
      desEncryptDecrypt.main();
      desUnscrunching.main();
    }
  }
};

behavior Design(i_receiver c1, 
                i_receiver fifo1, 
                i_sender is1, 
                i_sender is2)
{
  // Local variables shared between encryption and modulation.
  c_double_handshake ch1;

  // Behavior instantiation
  Des encryption(c1, fifo1, ch1);
  ofdm modulation(ch1, is1, is2 );

  void main()
  {
    par 
    { 
      encryption.main(); 
      modulation.main();
    }
  }
};

behavior Output(i_receiver ir1, i_receiver ir2, out event done)
{
  const char* bName = "Output";
  float out_re;
  float out_im;
  int i;
  void main()
  {
    while(1) {
     for (i=0; i<16; i++)
     {
      ir1.receive (&out_re, sizeof(float));
      ir2.receive (&out_im, sizeof(float));
      printf("%s: result_re: %f, result.im: %f\n", bName, out_re, out_im);
     }
    
    notify (done);
   }
  }
};

behavior Main()
{
  // Communication elements
  const unsigned long queueSize = 100;
  c_queue fifo1(queueSize);
  c_double_handshake c1, c2, c3;
  event done;
  
  // Behavior instantiation
  Stimulus input(c1, fifo1, done);
  Design application(c1, fifo1, c2, c3);
  Output result(c2, c3, done);

  int main() 
  {
    par {
      input.main(); 
      application.main(); 
      result.main(); 
    }
    return 0;
  }
};
